
/**
 * 描述 当前函数仅限支持 Node环境
 * 时间：2022-04-11
 * 作者：自夏
 */

import { networkInterfaces, tmpdir } from 'os'
import { createServer } from 'net'
import { readdirSync, readFile, statSync, writeFile, writeFileSync } from 'fs'
import { join } from 'path';
import { China } from './JSONDTA'
import { UtilString } from '../util/UtilString';
import { writeBase64Img, fileType } from '../Interface/util_Interface';

export class systemOs {
  /**
   * 获取本机ip
   * @returns 返回本机IP
   */
  static IP() {
    const interfaces: any = networkInterfaces();
    for (let devName in interfaces) {
      let iface = interfaces[devName];
      for (let i = 0; i < iface.length; i++) {
        let alias = iface[i];
        if (
          alias.family === "IPv4" &&
          alias.address !== "127.0.0.1" &&
          !alias.internal
        ) {
          return alias.address;
        }
      }
    }
  }

  static getClientIP(req: any): string {
    let client: any =
      req.headers["x-forwarded-for"] || // 判断是否有反向代理 IP
      req.connection.remoteAddress || // 判断 connection 的远程 IP
      req.socket.remoteAddress || // 判断后端的 socket 的 IP
      req.connection.socket.remoteAddress;
    return UtilString.getSpecifyTxt(client, ":") as unknown as fileType["suffix"];
  }
}


type filed = {
  FileUrl: string,
  paths: Array<string>,
  FileName: string
  type: string | undefined
}
/**
 * 获取指定目录下面所有的文件名称以及路径
 * @param startPath  String
 * 返回指定目录下面的文件路径
 */
export function findSync(startPath: string) {
  return new Promise<Array<filed>>((resolve, reject) => {
    let result: Array<filed> = [];
    try {
      let finder = (path: string) => {
        let files = readdirSync(path);
        files.forEach((val: string, _index: number, _list: Array<string>) => {
          let fPath = join(path, val), stats = statSync(fPath);
          if (stats.isDirectory()) finder(fPath);
          if (stats.isFile()) {
            let add = fPath?.replace(/\\/g, "/");
            result.push({
              FileUrl: add,
              paths: add.split("/"),
              FileName: val,
              type: UtilString.getSpecifyTxt(val, ".")?.suffix,
            });
          }
        });
      };
      finder(startPath);
      resolve(result)
    } catch (error) {
      reject(error)
    }
  })
}

/**
 * 查看某个端口是否被使用
 * @param port 端口号
 * @returns
 */
export function portIsOccupied(port: number) {
  // 创建服务并监听该端口
  const server = createServer().listen(port);
  let isPort = false;
  server.on("listening", function () {
    // 执行这块代码说明端口未被占用
    // server.close(); // 关闭服务
    isPort = false;
    console.log("端口【" + port + "】 正常"); // 控制台输出信息
  });

  server.on("error", function (err: any) {
    if (err.code === "EADDRINUSE") {
      isPort = true;
      console.log("端口【" + port + "】已经被使用");
    }
  });

  server.close();
  return isPort;
}


/**
 *
 * @param url string 要读的地址
 * @param coding 编码类型 空字符串默认为 base64 可以指定其他字符
 * @returns
 */
export function readFileBase64Sync(url: string, coding?: string | undefined): Promise<string> {
  return new Promise<string>((resolve, reject) => {
    if (url) {
      try {
        readFile(url, (err: Error | null, data: Buffer) => {
          if (err) return reject(err);
          // @ts-ignore
          const selfieAnimebase64Img = coding ? resolve(new Buffer.from(data).toString(coding || "base64")) : new Buffer.from(data);
          resolve(selfieAnimebase64Img);
        });
      } catch (error) {
        reject(error);
      }
    } else {
      reject("没有读取的文件路径");
    }
  });
}



/**
 * 把流或者是字符串成文件
 * @param ImageData {savePaht 保存地址 file 保存数据 字符串或者二进制数据 fileName 文件名称可选，默认为当前时间戳 type 文件保存类型，如果为data:image 开始的默认保存为 png}
 * @returns
 */
export function writeBase64(ImageData: writeBase64Img): Promise<string> {
  return new Promise((resolve, reject) => {
    try {
      const { savePaht, file, type, fileName } = ImageData;
      if (file && savePaht) {
        let base64Data: string, dataBuffer: ArrayBufferView | string, saveName: string;
        if (file.startsWith(', ""),
            // @ts-ignore
            dataBuffer = new Buffer.from(base64Data, 'base64'),
            saveName = `${savePaht}/${fileName || new Date().getTime()}.${type || "png"}`;
        }
        // @ts-ignore
        writeFile(saveName, dataBuffer || image, (error: any) => {
          error ? reject(error) : resolve(saveName);
        });
      } else {
        reject({ msg: '缺少数据源', data: ImageData })
      }

    } catch (error) {
      reject({ msg: 'writeBase64 写入错误', data: ImageData })
    }
  });
}


// class unscrambler {
//   constructor(setDecode: Function, getDecode: Function) {
//     Object.assign(this, { setDecode, getDecode });
//   }
// }
// // 设置加密参数
// const setDecode = (setObj: string) => {
//   try {
//     if (Object.keys(setObj).length > 0) setObj = JSON.stringify(setObj);
//     let set = encodeURIComponent(setObj), result = Buffer.from(set, 'binary').toString('base64');
//     return result;
//   } catch (error) {
//     return null
//   }
// };

// //解密参数
// const getDecode = (getData: string) => {
//   let jieMi = Buffer.from(getData, 'base64').toString('binary'),
//     jieM = decodeURIComponent(jieMi);
//   try {
//     return JSON.parse(jieM)
//   } catch (e) {
//     return null
//   }
// };

// export const decode = new unscrambler(setDecode, getDecode);




/**
 *  Promise NodeJS Promise拒绝处理收集方法
 * @param hadeleRejection 错误处理的回掉函数
 * @param time 间隔x收集一次 1000 = 1S 默认 10000
 */
export const EventPollingPromiseError = (hadeleRejection: Function, time: number) => {

  let poccibyUnhandledRejection = new Map();
  process.on("unhandledRejection", (reason: any, promise: any) => {
    poccibyUnhandledRejection.set(reason, promise);
  });

  process.on("rejectionHandled", (promise: any) => {
    poccibyUnhandledRejection.delete(promise);
  });


  setInterval(() => {
    poccibyUnhandledRejection.forEach((reason, promise) => {
      hadeleRejection(promise, reason, poccibyUnhandledRejection)
    });

    poccibyUnhandledRejection.clear();
  }, time || 10000);

};


type User = {
  province: string,
  city: string,
  area: string,
  provinceAndCity: string,
  dateBirthday: {
    year: number, month: number, day: number,
    Birthday: string
    sex: string
    age: number
    astrology: string | undefined,
    isLike: string | null
  }
}

/**
 * 读取身份证详细信息
 */
export class ChinaIdUserinfo {
  /**
   *
   * @param uuid
   * @returns {}
   */
  static getProvinceAndCity = (uuid: string) => {
    return new Promise<User>((resolve, reject) => {
      if (uuid == undefined || uuid.length < 18 || uuid.length > 18) {
        return reject({ msg: '身份证号错误!', data: uuid });
      } else {
        const provinceNo = uuid.substring(0, 2),
          cityNo = uuid.substring(0, 4),
          countyNo = uuid.substring(0, 6);

        const yer: number = new Date().getFullYear();
        // @ts-ignore
        const provinceName = China.province[provinceNo], cityName = China.city[cityNo], countyName = China.county[countyNo];
        const { province, city, area } = { province: provinceName || '', city: cityName || '', area: countyName || '', };
        let birthday = uuid.substring(6, 14), year = parseInt(birthday.substring(0, 4)), month = parseInt(birthday.substring(4, 6)), day = parseInt(birthday.substring(6, 8)), isSex: number = parseInt(uuid.substring(16, 17)), sex = isSex & 1 ? '男' : '女';
        resolve({
          province,
          city,
          area,
          provinceAndCity: `${province}-${city}-${area}`,
          dateBirthday: {
            year, month, day,
            Birthday: `${year}-${month}-${day}`, sex, age: (yer - year),
            astrology: this.astrology(month, day),
            isLike: this.isLike(year)
          }
        })
      }
    })
  }
  /**
   * 根据月份和天获取星座
   * @param year 月 1
   * @param day 天 19
   * @returns 水瓶座
   */
  static astrology = (year: number, day: number) => {
    if (year == 1 && day >= 20 || year == 2 && day <= 18) return "水瓶座";
    if (year == 1 && day > 31) return "Huh?"
    if (year == 2 && day >= 19 || year == 3 && day <= 20) return "双鱼座";
    if (year == 2 && day > 29) return "Say what?";
    if (year == 3 && day >= 21 || year == 4 && day <= 19) return "白羊座";
    if (year == 3 && day > 31) return "OK.  Whatever."
    if (year == 4 && day >= 20 || year == 5 && day <= 20) return "金牛座";
    if (year == 4 && day > 30) return "I'm soooo sorry!";
    if (year == 5 && day >= 21 || year == 6 && day <= 21) return "双子座";
    if (year == 5 && day > 31) return "Umm ... no.";
    if (year == 6 && day >= 22 || year == 7 && day <= 22) return "巨蟹座";
    if (year == 6 && day > 30) return "Sorry.";
    if (year == 7 && day >= 23 || year == 8 && day <= 22) return "狮子座";
    if (year == 7 && day > 31) return "Excuse me?";
    if (year == 8 && day >= 23 || year == 9 && day <= 22) return "处女座";
    if (year == 8 && day > 31) return "Yeah. Right.";
    if (year == 9 && day >= 23 || year == 10 && day <= 22) return "天秤座";
    if (year == 9 && day > 30) return "Try Again.";
    if (year == 10 && day >= 23 || year == 11 && day <= 21) return "天蝎座";
    if (year == 10 && day > 31) return "Forget it!";
    if (year == 11 && day >= 22 || year == 12 && day <= 21) return "射手座";
    if (year == 11 && day > 30) return "Invalid day";
    if (year == 12 && day >= 22 || year == 1 && day <= 19) return "摩羯座";
    if (year == 12 && day > 31) return "No way!";
  }
  /**
   * 根据当前年获取生肖
   * @param yyyy 2020
   * @returns
   */
  static isLike = (yyyy: any) => {
    var arr = ['猴', '鸡', '狗', '猪', '鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊'];
    return /^\d{4}$/.test(yyyy) ? arr[yyyy % 12] : null;
  }
}


type dataType = {
  url: string
  key: string
  data: Object | any;
}

/**
 * 函数重载
 * @param value 接需要序列化的对象
 */
function URLFlat(value: dataType): string
function URLFlat(value: string, key: string, ObjeData: any): string
function URLFlat(value: any, Autokey = '', OBj = '',): string {

  /**
   * 对象转为字符串
   * @param data
   * @returns
   */

  const ObjAndJSONFlat = (data: Object | any) => {
    let str: string = "";
    if (data instanceof Object) {
      for (let k in data) {
        str += `&${k}=${data[k]}`
      }
    }
    return str;
  }
  if (value instanceof Object) {
    const { url, key, data } = value;
    return `${url}?${key}${ObjAndJSONFlat(data)}`
  } else {
    return `${value}?${Autokey}${ObjAndJSONFlat(OBj)}`
  }
}

